#include <jni.h>
#include <string>

extern "C"
{
#include "librtmp/rtmp.h"
#include "android/log.h"
}

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "RTMP", __VA_ARGS__)

RTMP *rtmp = nullptr;

int16_t spsLen = 0;
int8_t *sps = 0;
int16_t ppsLen = 0;
int8_t *pps = 0;

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_soriya_nestlive_service_ScreenLiveService_connect(JNIEnv *env, jobject thiz, jstring url_) {
    const char *url = env->GetStringUTFChars(url_, 0);

    int ret = 0;

    do {
        // 申请内存(创建RTMP对象)
        rtmp = RTMP_Alloc();
        if (!rtmp) {
            LOGI("申请RTMP内存失败");
            break;
        }
        // 初始化RTMP
        RTMP_Init(rtmp);
        // 配置RTMP
        rtmp->Link.timeout = 10;

        // 设置RTMP地址
        ret = RTMP_SetupURL(rtmp, (char *) url);
        if (!ret) {
            LOGI("设置RTMP推流地址 %s 失败", url);
            break;
        }
        // 允许写出RTMP(推流)
        RTMP_EnableWrite(rtmp);
        // 连接服务器
        ret = RTMP_Connect(rtmp, 0);
        if (!ret) {
            LOGI("连接RTMP服务器失败 %s", url);
            break;
        }
        // 连接流
        ret = RTMP_ConnectStream(rtmp, 0);
        if (!ret) {
            LOGI("连接流失败");
            break;
        }
        LOGI("连接成功: %s", url);
    } while (false);

    env->ReleaseStringUTFChars(url_, url);
    return ret;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_soriya_nestlive_service_ScreenLiveService_close(JNIEnv *env, jobject thiz) {
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
    }
    if (sps) {
        free(sps);
    }
    if (pps) {
        free(pps);
    }
}

int sendVideo(int8_t *buf, int len, long tms) {

    RTMPPacket *packet = 0;

    do {
        // buf[0 - 3] 起始码 00 00 00 01
        // 取低五位，NALU类型
        if ((buf[4] & 0x1F) == 7) {
            LOGI("SPS PPS");
            // SPS PPS
            for (int i = 0; i < len; i++) {
                if (i + 4 < len) {
                    if (buf[i] == 0x00 && buf[i + 1] == 0x00 && buf[i + 2] == 0x00 && buf[i + 3] == 0x01) {
                        if (buf[i + 4] == 0x68) {
                            spsLen = i - 4;
                            sps = static_cast<int8_t *>(malloc(spsLen));
                            memcpy(sps, buf + 4, spsLen);

                            ppsLen = len - (4 + spsLen) - 4;
                            pps = static_cast<int8_t *>(malloc(ppsLen));
                            memcpy(pps, buf + 4 + spsLen + 4, ppsLen);
                            break;
                        }
                    }
                }
            }
            return 0;
        }
        if ((buf[4] & 0x1F) == 5) {
            LOGI("关键帧");
            // 关键帧
            // 关键帧发送之前发送SPS PPS
            packet = (RTMPPacket *) malloc(sizeof(RTMPPacket)); // C写法

//        RTMPPacket *packet = new RTMPPacket; // C++写法
            int rtmpPackagesSize = 10 + 3 + spsLen + 3 + ppsLen;
            RTMPPacket_Alloc(packet, rtmpPackagesSize);
            int nextPosition = 0;

            // 帧类型数据 : 分为两部分;
            // 前 4 位表示帧类型, 1 表示关键帧, 2 表示普通帧
            // 后 4 位表示编码类型, 7 表示 AVC 视频编码
            packet->m_body[nextPosition++] = 0x17;

            // 数据类型, 00 表示 AVC 序列头
            packet->m_body[nextPosition++] = 0x00;

            // 合成时间, 一般设置 00 00 00
            packet->m_body[nextPosition++] = 0x00;
            packet->m_body[nextPosition++] = 0x00;
            packet->m_body[nextPosition++] = 0x00;

            // 版本信息
            packet->m_body[nextPosition++] = 0x01;
            LOGI("Test Packet: %p", packet);
            LOGI("Test SPS: %p", sps);
            // 编码规格
            packet->m_body[nextPosition++] = sps[1];
            packet->m_body[nextPosition++] = sps[2];
            packet->m_body[nextPosition++] = sps[3];

            // NALU 长度
            packet->m_body[nextPosition++] = 0xFF;

            // SPS 个数
            packet->m_body[nextPosition++] = 0xE1;

            // SPS 长度, 占 2 字节
            // 设置长度的高位
            packet->m_body[nextPosition++] = (spsLen >> 8) & 0xFF;
            // 设置长度的低位
            packet->m_body[nextPosition++] = spsLen & 0xFF;

            // 拷贝 SPS 数据
            // 将 SPS 数据拷贝到 rtmpPacket->m_body[nextPosition] 地址中
            memcpy(&packet->m_body[nextPosition], sps, spsLen);
            // 累加 SPS 长度信息
            nextPosition += spsLen;

            // PPS 个数
            packet->m_body[nextPosition++] = 0x01;

            // PPS 数据的长度, 占 2 字节
            // 设置长度的高位
            packet->m_body[nextPosition++] = (ppsLen >> 8) & 0xFF;
            // 设置长度的低位
            packet->m_body[nextPosition++] = (ppsLen) & 0xFF;

            // 拷贝 SPS 数据
            memcpy(&packet->m_body[nextPosition], pps, ppsLen);

            // 设置 RTMP 包类型, 视频类型数据
            packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
            // 设置 RTMP 包长度
            packet->m_nBodySize = rtmpPackagesSize;
            // 分配 RTMP 通道, 随意分配
            packet->m_nChannel = 10;
            // 设置视频时间戳, 如果是 SPP PPS 数据, 没有时间戳
            packet->m_nTimeStamp = 0;
            // 设置绝对时间, 对于 SPS PPS 赋值 0 即可
            packet->m_hasAbsTimestamp = 0;
            // 设置头类型, 随意设置一个
            packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;

            int ret = RTMP_SendPacket(rtmp, packet, 1);
            if (!ret) {
                LOGI("SPS发送失败");
                break;
            }
//            RTMPPacket_Free(packet);
//            free(packet);
        }

        // =====


        // 数据帧
        if (buf[2] == 0x00) {
            // 四字节的分隔符
            buf += 4; // 移动指针
            len -= 4;
        }
        if (buf[2] == 0x01) {
            // 三字节的分隔符
            buf += 3; // 移动指针
            len -= 4;
        }

        int rtmpPackagesSize = len + 9;
        packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
        RTMPPacket_Alloc(packet, rtmpPackagesSize);

        packet->m_body[0] = 0x27; // 非关键帧
        if ((buf[0] & 0x1F) == 5) {
            // 关键帧
            packet->m_body[0] = 0x17;
        }

        // 设置包类型, 01 是数据帧, 00 是 AVC 序列头封装 SPS PPS 数据
        packet->m_body[1] = 0x01;
        // 合成时间戳, AVC 数据直接赋值 00 00 00
        packet->m_body[2] = 0x00;
        packet->m_body[3] = 0x00;
        packet->m_body[4] = 0x00;

        // 数据长度, 需要使用 4 位表示
        packet->m_body[5] = (len >> 24) & 0xFF;
        packet->m_body[6] = (len >> 16) & 0xFF;
        packet->m_body[7] = (len >> 8) & 0xFF;
        packet->m_body[8] = (len) & 0xFF;

        // H.264 数据帧数据
        memcpy(&packet->m_body[9], buf, len);

        // 设置 RTMP 包类型, 视频类型数据
        packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
        // 设置 RTMP 包长度
        packet->m_nBodySize = rtmpPackagesSize;
        // 分配 RTMP 通道, 随意分配
        packet->m_nChannel = 10;
        // 设置绝对时间, 对于 SPS PPS 赋值 0 即可
        packet->m_hasAbsTimestamp = 0;
        packet->m_nTimeStamp = tms;
        // 设置头类型, 随意设置一个
        packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;

        int ret = RTMP_SendPacket(rtmp, packet, 1);
        if (!ret) {
            LOGI("视频发送失败, Status: %d", ret);
            break;
        }
    } while (false);

//    LOGI("Packet: %p", packet);
    RTMPPacket_Free(packet);
    free(packet);

    return 1;
}

int sendAudio(int8_t *buf, int len, long tms, int type) {
    int rtmpPacketSize = len + 2;

    RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
    RTMPPacket_Alloc(packet, rtmpPacketSize);

    packet->m_body[0] = 0xAF; // 单声道为AE
    packet->m_body[1] = 0x01; // 音频数据
    if (type == 1) {
        packet->m_body[1] = 0x00;
    }

    memcpy(&packet->m_body[2], buf, len);

    // 设置绝对时间, 一般设置 0 即可
    packet->m_hasAbsTimestamp = 0;
    // 设置 RTMP 数据包大小
    packet->m_nBodySize = rtmpPacketSize;
    // 设置 RTMP 包类型, 音频类型数据
    packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
    // 分配 RTMP 通道, 该值随意设置, 建议在视频 H.264 通道基础上加 1
    packet->m_nChannel = 11;
    // // 设置头类型, 随意设置一个
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nTimeStamp = tms;

    int ret = RTMP_SendPacket(rtmp, packet, 1);
    if (!ret) {
        LOGI("音频发送失败, Status: %d", ret);
    }

    RTMPPacket_Free(packet);
    free(packet);

    return 1;
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_soriya_nestlive_service_ScreenLiveService_pushData(JNIEnv *env, jobject thiz, jbyteArray data_, jint len, jint type, jlong tms) {
    jbyte *data = env->GetByteArrayElements(data_, nullptr);

//    LOGI("IsConnected: %d", RTMP_IsConnected(rtmp));


    // type为StreamConstant.RTMP_PUSH_XXX
    switch (type) {
        // video
        case 0:
            sendVideo(data, len, tms);
            break;
        // audio encode
        case 1:
        // audio data
        case 2:
            sendAudio(data, len, tms, type);
        break;
    }

    env->ReleaseByteArrayElements(data_, data, 0);
    return 1;
}
